package com.wan.core.timer.task;

import com.wan.core.timer.TimeWheelException;
import com.wan.core.timer.TimerWheel;
import com.wan.core.timer.bucket.Bucket;
import com.wan.core.timer.rule.WRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * 任务节点
 * @author wwh
 * @date 2022/6/28 17:15
 */
public class TaskNode {
    private static final Logger logger = LoggerFactory.getLogger(TaskNode.class);
    /** 任务 */
    private WTask task;
    /** 定时规则 */
    private WRule rule;
    /** 执行次数上限, -1代表无限制 */
    private int maxTime;
    /** 当前执行次数 */
    private int current;
    /** 间隔周期 */
    private int period;
    /** 执行时间点 */
    private LocalDateTime executeTime;
    /** 所在槽位 */
    private Bucket bucket;
    /** 轮数,最上层时间轮超出周期时 */
    private long round;
    /** 线程池 */
    private Executor executor;

    protected TaskNode(WRule rule, int maxTime, int period, LocalDateTime executeTime,WTask task,Executor executor) {
        this.task = task;
        this.rule = rule;
        this.maxTime = maxTime;
        this.period = period;
        this.executeTime = executeTime;
        this.executor = executor;
    }

    public WTask getTask() {
        return task;
    }

    public void setTask(WTask task) {
        this.task = task;
    }

    public WRule getRule() {
        return rule;
    }

    public void setRule(WRule rule) {
        this.rule = rule;
    }

    public int getMaxTime() {
        return maxTime;
    }

    public void setMaxTime(int maxTime) {
        this.maxTime = maxTime;
    }

    public int getCurrent() {
        return current;
    }

    public int getPeriod() {
        return period;
    }

    public void setPeriod(int period) {
        this.period = period;
    }

    public LocalDateTime getExecuteTime() {
        return executeTime;
    }

    public void setBucket(Bucket bucket) {
        this.bucket = bucket;
    }

    public long getRound() {
        return round;
    }

    public void setRound(long round) {
        this.round = round;
    }

    public TaskNode setExecutor(Executor executor) {
        this.executor = executor;
        return this;
    }

    /**
     * 任务执行
     */
    public void execute(){
        if(bucket == null){
            throw new TimeWheelException("未放入时间轮的任务无法执行,task:" + this);
        }
        Executor executor = this.executor == null ? bucket.getTimerWheel().getExecutor() : this.executor;
        executor.execute(() -> {
            try {
                this.task.run(this);
            } catch (Exception exception) {
                logger.error("定时任务执行异常,task->{}",this,exception);
            }finally {
                try {
                    current++;
                    if(bucket != null){
                        //任务重新加入一定要从最下层时间轮加入
                        TimerWheel minWheel = this.bucket.getTimerWheel().findMinWheel();
                        this.cancel();
                        if(maxTime < 0 || current < maxTime) {
                            LocalDateTime nextTime = rule.nextTime(executeTime, period);
                            //如果下一次执行时间不为空，则赋值新的执行时间，重新放入时间轮
                            if (nextTime != null) {
                                this.executeTime = nextTime;
                                minWheel.addTask(this);
                            }
                        }
                    }
                }catch (Exception e){
                    logger.error("定时任务后置处理异常,task->{}",this,e);
                }
            }
        });
    }

    public boolean isExpired(){
        LocalDateTime now = LocalDateTime.now();
        return executeTime.isBefore(now) || executeTime.isEqual(now);
    }

    /**
     * 取消任务或切换层级
     */
    public void cancel(){
        if(bucket != null){
            this.bucket.remove(this);
        }
        this.bucket = null;
        this.round = 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TaskNode taskNode = (TaskNode) o;
        return maxTime == taskNode.maxTime && current == taskNode.current && period == taskNode.period && Objects.equals(task, taskNode.task) && Objects.equals(rule, taskNode.rule) && Objects.equals(executeTime, taskNode.executeTime) && Objects.equals(bucket, taskNode.bucket);
    }

    @Override
    public int hashCode() {
        return Objects.hash(task, rule, maxTime, current, period, executeTime, bucket);
    }

    @Override
    public String toString() {
        return "TaskNode{" +
                "task=" + task +
                ", rule=" + rule +
                ", maxTime=" + maxTime +
                ", current=" + current +
                ", period=" + period +
                ", executeTime=" + executeTime +
                ", bucket=" + bucket +
                ", round=" + round +
                '}';
    }
}
